frontend/pages/e/[uuid].tsx (view raw)
1import {useState, useReducer, useEffect} from 'react';
2import Box from '@material-ui/core/Box';
3import {makeStyles, useTheme} from '@material-ui/core/styles';
4import {useTranslation} from 'react-i18next';
5import {initializeApollo} from '../../lib/apolloClient';
6import useToastStore from '../../stores/useToastStore';
7import useEventStore from '../../stores/useEventStore';
8import Layout from '../../layouts/Default';
9import AddToMyEventDialog from '../../containers/AddToMyEventDialog';
10import TravelColumns from '../../containers/TravelColumns';
11import NewTravelDialog from '../../containers/NewTravelDialog';
12import VehicleChoiceDialog from '../../containers/VehicleChoiceDialog';
13import WelcomeDialog from '../../containers/WelcomeDialog';
14import EventBar from '../../containers/EventBar';
15import Loading from '../../containers/Loading';
16import OnBoardingTour from '../../containers/OnBoardingTour';
17import {
18 useUpdateEventMutation,
19 Event as EventType,
20 useEventByUuidQuery,
21 EventByUuidDocument,
22 EditEventInput,
23 useFindUserVehiclesQuery,
24} from '../../generated/graphql';
25import ErrorPage from '../_error';
26import useProfile from '../../hooks/useProfile';
27import Fab from '../../containers/Fab';
28import useMediaQuery from '@material-ui/core/useMediaQuery';
29import useBannerStore from '../../stores/useBannerStore';
30import DrawerMenu from '../../containers/DrawerMenu';
31
32const POLL_INTERVAL = 10000;
33
34interface Props {
35 event: EventType;
36 eventUUID: string;
37}
38
39const EventPage = props => {
40 const {t} = useTranslation();
41 const {event} = props;
42 if (!event) return <ErrorPage statusCode={404} title={t`event.not_found`} />;
43 return <Event {...props} />;
44};
45
46const Event = (props: Props) => {
47 const {eventUUID} = props;
48 const bannerOffset = useBannerStore(s => s.offset);
49 const classes = useStyles({bannerOffset});
50 const theme = useTheme();
51 const {t} = useTranslation();
52 const {user} = useProfile();
53 const {data: {me: {profile: {vehicles = []} = {}} = {}} = {}, loading} =
54 useFindUserVehiclesQuery();
55 const addToast = useToastStore(s => s.addToast);
56 const setEvent = useEventStore(s => s.setEvent);
57 const eventUpdate = useEventStore(s => s.event);
58 const setIsEditing = useEventStore(s => s.setIsEditing);
59 const [updateEvent] = useUpdateEventMutation();
60 const [isAddToMyEvent, setIsAddToMyEvent] = useState(false);
61 const [openNewTravelContext, toggleNewTravel] = useState({opened: false});
62 const [openVehicleChoice, toggleVehicleChoice] = useReducer(i => !i, false);
63 const {data: {eventByUUID: event} = {}} = useEventByUuidQuery({
64 pollInterval: POLL_INTERVAL,
65 variables: {uuid: eventUUID},
66 });
67 const matches = useMediaQuery(theme.breakpoints.down('sm'));
68 const addCarClasses = matches ? 'tour_travel_add' : '';
69
70 useEffect(() => {
71 if (event) setEvent(event as EventType);
72 }, [event]);
73
74 const onSave = async e => {
75 try {
76 const {uuid, ...data} = eventUpdate;
77 const {id, __typename, travels, users, waitingList, ...input} = data;
78 await updateEvent({
79 variables: {uuid, eventUpdate: input as EditEventInput},
80 refetchQueries: ['eventByUUID'],
81 });
82 setIsEditing(false);
83 } catch (error) {
84 console.error(error);
85 addToast(t('event.errors.cant_update'));
86 }
87 };
88
89 const addTravelClickHandler =
90 user && vehicles?.length != 0
91 ? toggleVehicleChoice
92 : () => toggleNewTravel({opened: true});
93
94 if (!event || loading) return <Loading />;
95
96 return (
97 <Layout
98 className={classes.layout}
99 pageTitle={t('event.title', {title: event.name})}
100 menuTitle={t('event.title', {title: event.name})}
101 displayMenu={false}
102 >
103 <EventBar event={event} onAdd={setIsAddToMyEvent} onSave={onSave} />
104 <DrawerMenu />
105 <TravelColumns toggle={addTravelClickHandler} />
106 <Box className={classes.bottomRight}>
107 <Fab
108 onClick={addTravelClickHandler}
109 aria-label="add-car"
110 color="primary"
111 className={addCarClasses}
112 >
113 {t('travel.creation.title')}
114 </Fab>
115 </Box>
116 <NewTravelDialog
117 context={openNewTravelContext}
118 toggle={() => toggleNewTravel({opened: false})}
119 />
120 <VehicleChoiceDialog
121 open={openVehicleChoice}
122 toggle={toggleVehicleChoice}
123 toggleNewTravel={toggleNewTravel}
124 vehicles={vehicles}
125 />
126 <AddToMyEventDialog
127 event={event}
128 open={isAddToMyEvent}
129 onClose={() => setIsAddToMyEvent(false)}
130 />
131 <WelcomeDialog />
132 <OnBoardingTour />
133 </Layout>
134 );
135};
136
137export async function getServerSideProps(ctx) {
138 const {uuid} = ctx.query;
139 const apolloClient = initializeApollo();
140 const {data = {}} = await apolloClient.query({
141 query: EventByUuidDocument,
142 variables: {uuid},
143 });
144 const {eventByUUID: event} = data;
145 const {host = ''} = ctx.req.headers;
146
147 return {
148 props: {
149 event,
150 eventUUID: uuid,
151 metas: {
152 title: event?.name || '',
153 url: `https://${host}${ctx.resolvedUrl}`,
154 },
155 },
156 };
157}
158
159const useStyles = makeStyles(theme => ({
160 layout: ({bannerOffset}) => ({
161 paddingTop: theme.mixins.toolbar.minHeight + bannerOffset,
162 }),
163 bottomRight: {
164 position: 'absolute',
165 bottom: theme.spacing(1),
166 right: theme.spacing(6),
167 width: 200,
168 [theme.breakpoints.down('sm')]: {
169 right: theme.spacing(1),
170 },
171 },
172}));
173
174export default EventPage;